/*!
 * \file WtTWapExeUnit.cpp
 * \project	WonderTrader
 *
 * \author Wesley
 * \date 2020/03/30
 *
 * \brief TWAP执行单元实现
 * \details 实现时间加权平均价格(TWAP)执行算法，在指定时间内均匀分布订单
 */
#include "WtTWapExeUnit.h"

#include "../Share/TimeUtils.hpp"
#include "../Includes/WTSVariant.hpp"
#include "../Includes/WTSContractInfo.hpp"
#include "../Share/decimal.h"
#include "../Share/fmtlib.h"

#include <math.h>


extern const char* FACT_NAME;


/**
 * @brief 构造函数
 * @details 初始化所有成员变量为默认值
 */
WtTWapExeUnit::WtTWapExeUnit()
	: _last_tick(NULL)
	, _comm_info(NULL)
	, _ord_sticky(0)
	, _cancel_cnt(0)
	, _channel_ready(false)
	, _last_fire_time(0)
	, _fired_times(0)
	, _total_times(0)
	, _total_secs(0)
	, _price_mode(0)
	, _price_offset(0)
	, _target_pos(0)
{
}


/**
 * @brief 析构函数
 * @details 释放持有的资源，包括tick数据和品种信息
 */
WtTWapExeUnit::~WtTWapExeUnit()
{
	if (_last_tick)
		_last_tick->release();

	if (_comm_info)
		_comm_info->release();
}

/**
 * @brief 获取执行工厂名称
 * @return 返回所属执行工厂的名称
 */
const char* WtTWapExeUnit::getFactName()
{
	return FACT_NAME;
}

/**
 * @brief 获取执行单元名称
 * @return 返回执行单元的名称
 */
const char* WtTWapExeUnit::getName()
{
	return "WtTWapExeUnit";
}

/**
 * @brief 初始化执行单元
 * @param ctx 执行上下文
 * @param stdCode 标准合约代码
 * @param cfg 配置参数
 * @details 从配置中读取TWAP执行参数，包括总时长、执行次数、价格模式等
 */
void WtTWapExeUnit::init(ExecuteContext* ctx, const char* stdCode, WTSVariant* cfg)
{
	ExecuteUnit::init(ctx, stdCode, cfg);

	_comm_info = ctx->getCommodityInfo(stdCode);
	if (_comm_info)
		_comm_info->retain();

	_ord_sticky = cfg->getUInt32("ord_sticky");
	_total_secs = cfg->getUInt32("total_secs");
	_tail_secs = cfg->getUInt32("tail_secs");
	_total_times = cfg->getUInt32("total_times");
	_price_mode = cfg->getUInt32("price_mode");
	_price_offset = cfg->getUInt32("price_offset");

	_fire_span = (_total_secs - _tail_secs) / _total_times;		///< 单次发单时间间隔，要去掉尾部时间，否则的话，最后剩余的数量会一次性发出去的

	ctx->writeLog(fmt::format("执行单元WtTWapExeUnit[{}] 初始化完成，挂单时长 {} 秒，执行时长 {} 秒，尾部时长 {} 秒", stdCode, _ord_sticky, _total_secs, _tail_secs).c_str());
}

/**
 * @brief 订单回报处理
 * @param localid 本地订单号
 * @param stdCode 合约代码
 * @param isBuy 买卖方向
 * @param leftover 剩余数量
 * @param price 委托价格
 * @param isCanceled 是否已撤销
 * @details 处理订单状态变化，更新订单监控器，处理撤单后的补单逻辑
 */
void WtTWapExeUnit::on_order(uint32_t localid, const char* stdCode, bool isBuy, double leftover, double price, bool isCanceled)
{
	if (!_orders_mon.has_order(localid))
		return;

	if (isCanceled || leftover == 0)
	{
		_orders_mon.erase_order(localid);

		if (_cancel_cnt > 0)
			_cancel_cnt--;

		_ctx->writeLog(fmt::format("Order {} updated cancelcnt -> {}", _cancel_cnt).c_str());
	}

	// 如果全部订单都已撤销，这时候一般情况下需要及时补单
	if (isCanceled && _cancel_cnt == 0)
	{
		double realPos = _ctx->getPosition(stdCode);
		if(!decimal::eq(realPos, _this_target))
		{
			// 立即补发，一般是加单补发
			fire_at_once(_this_target - realPos);
		}
	}
}

/**
 * @brief 交易通道就绪回调
 * @details 处理通道就绪后的订单状态同步，清理无效订单
 */
void WtTWapExeUnit::on_channel_ready()
{
	_channel_ready = true;
	double undone = _ctx->getUndoneQty(_code.c_str());
	if (undone != 0 && !_orders_mon.has_order())
	{
		// 说明这些未完成的是在重启之前，先撤销
		_ctx->writeLog(fmt::format("{} unmanaged orders of {}, cancel all", undone, _code).c_str());

		bool isBuy = (undone > 0);
		OrderIDs ids = _ctx->cancel(_code.c_str(), isBuy);
		_orders_mon.push_order(ids.data(), ids.size(), _ctx->getCurTime());
		_cancel_cnt += ids.size();

		_ctx->writeLog(fmt::format("Unmanaged order updated cancelcnt to {}", _cancel_cnt).c_str());
	}


	do_calc();
}

/**
 * @brief 交易通道丢失回调
 * @details 当前实现为空，可根据需要添加处理逻辑
 */
void WtTWapExeUnit::on_channel_lost()
{

}

/**
 * @brief tick数据回调处理
 * @param newTick 新的tick数据
 * @details 更新最新tick数据，检查订单超时，触发TWAP执行计算
 */
void WtTWapExeUnit::on_tick(WTSTickData* newTick)
{
	if (newTick == NULL || _code.compare(newTick->code()) != 0)
		return;

	bool isFirstTick = false;
	// 先把原来的tick释放掉
	if (_last_tick)
		_last_tick->release();
	else
		isFirstTick = true;

	// 新的tick数据，要保留
	_last_tick = newTick;
	_last_tick->retain();

	/*
	 * 这里可以考虑一下
	 * 如果写的过于频繁，可能一次订单过去的时候并没有达到目标仓位
	 * 那么新的订单就会根据新数据来进行实时的重新触发执行逻辑
	 */

	if (isFirstTick)	// 如果是第一个tick，检查目标仓位，看是否需要下单
	{
		double newVol = _target_pos;
		const char* stdCode = _code.c_str();
		double undone = _ctx->getUndoneQty(stdCode);
		double realPos = _ctx->getPosition(stdCode);
		if (!decimal::eq(newVol, undone + realPos)) // 仓位变化要处理
		{
			do_calc();
		}
	}
	else
	{
		uint64_t now = _ctx->getCurTime();
		bool hasCancel = false;
		if (_ord_sticky != 0 && _orders_mon.has_order())
		{
			_orders_mon.check_orders(_ord_sticky, now, [this, &hasCancel](uint32_t localid) {
				if (_ctx->cancel(localid))
				{
					_cancel_cnt++;
					_ctx->writeLog(fmt::format("Order expired, cancelcnt updated to {}", _cancel_cnt).c_str());
					hasCancel = true;
				}
			});

		}
		
		if (!hasCancel && (now - _last_fire_time >= _fire_span))
		{
			do_calc();
		}
	}
}

/**
 * @brief 成交回报处理
 * @param localid 本地订单号
 * @param stdCode 合约代码
 * @param isBuy 买卖方向
 * @param vol 成交数量
 * @param price 成交价格
 * @details 当前实现为空，成交处理在ontick里触发重新计算
 */
void WtTWapExeUnit::on_trade(uint32_t localid, const char* stdCode, bool isBuy, double vol, double price)
{
	// 这里什么都不做，统一在ontick里触发重新计算
}

/**
 * @brief 委托回报处理
 * @param localid 本地订单号
 * @param stdCode 合约代码
 * @param bSuccess 委托是否成功
 * @param message 回报消息
 * @details 处理委托失败的情况，从监控中移除失败订单
 */
void WtTWapExeUnit::on_entrust(uint32_t localid, const char* stdCode, bool bSuccess, const char* message)
{
	if (!bSuccess)
	{
		// 如果不是我发出去的订单，我就不管了
		if (!_orders_mon.has_order(localid))
			return;

		_orders_mon.erase_order(localid);

		do_calc();
	}
}

/**
 * @brief 立即执行指定数量
 * @param qty 要执行的数量
 * @details 用于紧急补单，通过偏移价格确保成交
 */
void WtTWapExeUnit::fire_at_once(double qty)
{
	if (decimal::eq(qty, 0))
		return;

	_last_tick->retain();
	WTSTickData* curTick = _last_tick;
	const char* code = _code.c_str();
	uint64_t now = TimeUtils::getLocalTimeNow();
	bool isBuy = decimal::gt(qty, 0);
	double targetPx = 0;
	// 根据价格模式配置，确定委托基准价格: 0-最新价，1-对手价，2-排队价
	if (_price_mode == 0)
	{
		targetPx = curTick->price();
	}
	else if (_price_mode == 1)
	{
		targetPx = isBuy ? curTick->bidprice(0) : curTick->askprice(0);
	}
	else // if(_price_mode == 2)
	{
		targetPx = isBuy ? curTick->askprice(0) : curTick->bidprice(0);
	}

	// 如果需要全部成交，价格偏移5跳，以保证执行
	targetPx += _comm_info->getPriceTick() * 5 * (isBuy ? 1 : -1);

	// 最后发出指令
	OrderIDs ids;
	if (qty > 0)
		ids = _ctx->buy(code, targetPx, abs(qty));
	else
		ids = _ctx->sell(code, targetPx, abs(qty));

	_orders_mon.push_order(ids.data(), ids.size(), now);

	curTick->release();
}

/**
 * @brief TWAP执行计算主逻辑
 * @details 核心TWAP算法，包括：
 *          - 检查通道状态和撤单状态
 *          - 计算当前应执行的数量
 *          - 根据剩余次数分配执行量
 *          - 处理最后一次全部执行
 */
void WtTWapExeUnit::do_calc()
{
	if (!_channel_ready)
		return;

	// 如果有在撤销的订单，不能进行下一次计算
	if (_cancel_cnt != 0)
	{
		_ctx->writeLog(fmt::format("{}存在未完成撤销指令，暂时退出算法执行", _code).c_str());
		return;
	}

	if (_last_tick == NULL)
	{
		_ctx->writeLog(fmt::format("{}没有最新tick数据，退出执行逻辑", _code).c_str());
		return;
	}

	const char* code = _code.c_str();

	double undone = _ctx->getUndoneQty(code);
	// 每一次发单要等上成交，如果还有未完成的，说明上一单没有完成
	if (undone != 0)
	{
		_ctx->writeLog(fmt::format("{}上一次有挂单未完成，暂时退出算法执行", _code).c_str());
		return;
	}

	double realPos = _ctx->getPosition(code);
	double diffQty = _target_pos - realPos;

	if (decimal::eq(diffQty, 0))
		return;

	uint32_t leftTimes = _total_times - _fired_times;

	bool bNeedShowHand = false;
	// 剩余次数为0，剩余数量不为0，说明要全部发出去了
	// 剩余次数为0，说明已经到了多次时间了，如果这时候还未完成，就需要梭哈
	if (leftTimes == 0 && !decimal::eq(diffQty, 0))
		bNeedShowHand = true;

	double curQty = 0;

	// 如果剩余次数为0，就需要全部下单
	// 否则，取max(剩余数量/剩余次数，1)的整数值，最小为一手，这里要注意放大倍数
	if (leftTimes == 0)
		curQty = diffQty;
	else
		curQty = std::max(1.0, round(abs(diffQty) / leftTimes)) * abs(diffQty) / diffQty;

	// 设定本次目标仓位
	_this_target = realPos + curQty;


	_last_tick->retain();
	WTSTickData* curTick = _last_tick;
	uint64_t now = TimeUtils::getLocalTimeNow();
	bool isBuy = decimal::gt(diffQty, 0);
	double targetPx = 0;
	// 根据价格模式配置，确定委托基准价格: 0-最新价，1-对手价，2-排队价
	if (_price_mode == 0)
	{
		targetPx = curTick->price();
	}
	else if(_price_mode == 1)
	{
		targetPx = isBuy ? curTick->bidprice(0) : curTick->askprice(0);
	}
	else // if(_price_mode == 2)
	{
		targetPx = isBuy ? curTick->askprice(0) : curTick->bidprice(0);
	}

	// 如果需要全部成交，价格偏移5跳，以保证执行
	if(bNeedShowHand) //  last showhand time
	{		
		targetPx += _comm_info->getPriceTick() * 5 * (isBuy ? 1 : -1);
	}
	else if(_price_offset != 0)	// 如果配置了价格偏移，也要处理一下
	{
		targetPx += _comm_info->getPriceTick() * _price_offset * (isBuy ? 1 : -1);
	}
	
	// 最后发出指令
	OrderIDs ids;
	if (curQty > 0)
		ids = _ctx->buy(code, targetPx, abs(curQty));
	else
		ids = _ctx->sell(code, targetPx, abs(curQty));

	_orders_mon.push_order(ids.data(), ids.size(), now);
	_last_fire_time = now;
	_fired_times += 1;

	curTick->release();
}

/**
 * @brief 设置目标仓位
 * @param stdCode 合约代码
 * @param newVol 新的目标仓位
 * @details 设置新的目标仓位，重置执行次数，触发TWAP计算
 */
void WtTWapExeUnit::set_position(const char* stdCode, double newVol)
{
	if (_code.compare(stdCode) != 0)
		return;

	if (decimal::eq(newVol, _target_pos))
		return;

	_target_pos = newVol;

	_fired_times = 0;

	do_calc();
}
